﻿using System;
using System.Collections.Generic;
using System.Drawing.Design;
using System.Linq;
using System.Windows.Forms.Design;
using devtm.Editor.TypeSelector;
using DslModeling = global::Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling;
using System.Windows.Forms;
using System.Reflection;
using System.Windows.Data;
using devtm.Verbatim.CustomConverters;
using System.ComponentModel;


namespace devtm.AutoMapper
{

    public partial class ListIValueConverterEditor : UITypeEditor
    {

        protected DslModeling.Store Store;
        protected ItemMap Instance;

        /// <summary>
        /// Edits the specified object's value using the editor style indicated by the <see cref="M:System.Drawing.Design.UITypeEditor.GetEditStyle"/> method.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that can be used to gain additional context information.</param>
        /// <param name="provider">An <see cref="T:System.IServiceProvider"/> that this editor can use to obtain services.</param>
        /// <param name="value">The object to edit.</param>
        /// <returns>
        /// The new value of the object. If the value of the object has not changed, this should return the same object it was passed.
        /// </returns>
        public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
        {

            IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            if (edSvc != null)
            {

                Instance = context.Instance as ItemMap;
                Store = Instance.Store;
                Mapper mapper = GetMapper(context);
                TypeElement typeElement = GetTypeElement(context, mapper);

                ListBox control = new ListBox();
                control.DisplayMember = "Name";
                control.ValueMember = "AssemblyQualifiedName";

                HashSet<string> namespaces = typeElement.MappingModel.GetNamespaces;

                List<Type> lst = null;

                if (Instance.TypeCast == CastType.IValueConverter)
                    lst = LoadTypes<IValueConverter>(typeElement, namespaces);

                else if (Instance.TypeCast == CastType.IConverter)
                    lst = LoadTypes<IConverter>(typeElement, namespaces);

                else if (Instance.TypeCast == CastType.TypeConverter)
                {

                    namespaces.Add("System");
                    namespaces.Add("System.ComponentModel");
                    namespaces.Add("System.Configuration");
                    namespaces.Add("System.Diagnostics.Design");
                    namespaces.Add("System.Security.Authentication.ExtendedProtection");

                    lst = LoadTypes<TypeConverter>(typeElement, namespaces);

                }

                control.DataSource = lst.OrderBy(c => c.Name).ToList();
                
                if (!String.IsNullOrEmpty((string)value))
                    control.SelectedValue = value;

                Close = () => edSvc.CloseDropDown();
                control.Click += control_Click;
                edSvc.DropDownControl(control);
                control.Click -= control_Click;
                value = string.Empty;

                try
                {
                    value = (control.SelectedItem as Type).AssemblyQualifiedName;
                }
                catch (Exception)
                {

                }
            }

            return value;

        }

        private List<Type> LoadTypes<T>(TypeElement typeElement, HashSet<string> namespaces)
        {
            List<Type> lst = ContainerTypes.GetFromReference(Store, namespaces).Where(c => c.IsSubclassOf(typeof(T))).ToList();
            lst.AddRange(ContainerTypes.GetFromSolution<T>(Store, namespaces));
            return lst;
        }


        private static Mapper GetMapper(System.ComponentModel.ITypeDescriptorContext context)
        {
            ItemMap p1 = context.Instance as ItemMap;
            return p1.Mapper;
        }


        void control_Click(object sender, EventArgs e)
        {

            if (sender != null)
                if ((sender as ListBox).SelectedItem != null)
                    Close();

        }

        protected Action Close;

        public override bool IsDropDownResizable
        {
            get
            {
                if (Instance != null && ShowDropDown(Instance))
                    return true;

                return false;
            }
        }

        public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
        {

            if (context != null && context.Instance != null)
                Instance = context.Instance as ItemMap;

            if (Instance != null && ShowDropDown(Instance))
                return UITypeEditorEditStyle.DropDown;

            return UITypeEditorEditStyle.None;
            //return base.GetEditStyle(context);
        }


        private bool ShowDropDown(ItemMap instance)
        {
            switch (instance.TypeCast)
            {
                case CastType.Implicit:
                case CastType.Custom:
                case CastType.Map:
                case CastType.None:
                case CastType.Explicit:
                case CastType.Collection:
                case CastType.List:
                case CastType.ObservableCollection:
                    return false;

                case CastType.IConverter:
                case CastType.IValueConverter:
                default:
                    return true;
            }
        }

            
        private static TypeElement GetTypeElement(System.ComponentModel.ITypeDescriptorContext context, Mapper mapper)
        {
            if (context.PropertyDescriptor.Name.Contains("Source"))
                return mapper.MappingSource.TypeElement;

            return mapper.MappingDestination.TypeElement;

        }


    }

}